/*
 * Copyright (c) 1994, 2013, Oracle and/or its affiliates. All rights reserved.
 * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 */

package java.io;
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;

/**
 * BufferedInputStream将缓冲输入并支持标记和重置方法的能力,添加到另一个输入流中。
 * 当创建bufferedinputstream时，将创建一个内部缓冲区数组。
 * 当读取或者跳过流中的字节时，如果需要，内部缓冲区将从所包含的输入流中重新一次读取许多字节。
 * mark操作会记住输入流中的一个点，而reset操作会在
 * 从包含的输入流中获取新字节之前，将自最近的mark操作以来读取的所有字节重新读取。
 *
 * @author  Arthur van Hoff
 * @since   JDK1.0
 */
public
class BufferedInputStream extends FilterInputStream {

    /**
     * 8192
     */
    private static int DEFAULT_BUFFER_SIZE = 8192;

    /**
     * 能分配的数组的最大大小。
     * 一些虚拟机在数组中保留一些头字。
     * 尝试分配更大的数组可能会导致  OutOfMemoryError:请求的数组大小超过VM限制
     */
    private static int MAX_BUFFER_SIZE = Integer.MAX_VALUE - 8;

    /**
     * 存储数据的内部缓冲区数组。
     * 必要时，可以用另一个大小不同的数组替换它。
     */
    protected volatile byte buf[];

    /**
     * 为buf提供compareAndSet的原子更新器。
     * 这是必要的，因为关闭可以是异步的。
     * 我们使用设置buf数组为null作为该流关闭的主要指示符。
     * (“in”字段在关闭时也为null。)
     */
    private static final
        AtomicReferenceFieldUpdater<BufferedInputStream, byte[]> bufUpdater =
        AtomicReferenceFieldUpdater.newUpdater
        (BufferedInputStream.class,  byte[].class, "buf");

    /**
     * 比缓冲区中最后一个有效字节的索引大1的索引。
     * 这个值总是在0到buf.length的范围内;
     * 元素buf[0]到buf[count-1]包含从底层输入流获得的缓冲输入数据。
     */
    protected int count;

    /**
     * 缓冲区中的当前位置。
     * 这是从buf数组中读取的下一个字符的索引。
     * <p>
     * 此值始终在0到count的范围内。
     * 如果它小于count，那么buf[pos]是下一个作为输入提供的字节;
     * 如果它等于count，那么下一个读操作或跳操作将需要从包含的输入流中提取更多字节。
     *
     * @see     java.io.BufferedInputStream#buf
     */
    protected int pos;

    /**
     * 调用最后一个mark方法时pos字段的值。
     * <p>
     * 这个值总是在-1到pos的范围内。如果在输入流中没有标记位置，这个字段是-1。
     * 如果在输入流中有一个标记位置，那么buf[markpos]是reset操作后作为输入提供的第一个字节。
     * 如果markpos不是-1，那么从位置 buf[markpos]到buf[pos -1]的所有字节必须保留在缓冲区数组中
     * (尽管它们可能会被分配到缓冲区数组中的另一个位置，并对count、pos和markpos的值进行适当的调整);
     * 除非和直到pos和markpos之间的差异超过marklimit，否则不能丢弃它们。
     *
     * @see     java.io.BufferedInputStream#mark(int)
     * @see     java.io.BufferedInputStream#pos
     */
    protected int markpos = -1;

    /**
     * 调用mark方法后在后续调用reset方法失败之前允许的最大预读量。
     * 每当pos和markpos之间的差异超过marklimit时，可以通过将markpos设置为-1来删除标记。
     *
     * @see     java.io.BufferedInputStream#mark(int)
     * @see     java.io.BufferedInputStream#reset()
     */
    protected int marklimit;

    /**
     * 检查以确保底层输入流没有因关闭而被空;
     * 如果没有关闭，则返回底层输入流;
     */
    private InputStream getInIfOpen() throws IOException {
        InputStream input = in;
        if (input == null)
            throw new IOException("Stream closed");
        return input;
    }

    /**
     * 检查以确保缓存数组没有因关闭而被空;
     * 如果没有关闭，则返回缓存数组;
     */
    private byte[] getBufIfOpen() throws IOException {
        byte[] buffer = buf;
        if (buffer == null)
            throw new IOException("Stream closed");
        return buffer;
    }

    /**
     * 创建一个BufferedInputStream并保存它的参数，即输入流in，以供以后使用。
     * 在内部创建并存储一个内部缓冲区数组，设置为buf，缓存默认大小为8192个byte。
     *
     * @param   in   the underlying input stream.
     */
    public BufferedInputStream(InputStream in) {
        this(in, DEFAULT_BUFFER_SIZE);
    }

    /**
     * 创建一个BufferedInputStream并保存它的参数，即输入流in，以供以后使用。
     * 创建一个大小为size的内部缓冲区数组并存储在buf中。
     *
     * @param   in     the underlying input stream.
     * @param   size   the buffer size.
     * @exception IllegalArgumentException if {@code size <= 0}.
     */
    public BufferedInputStream(InputStream in, int size) {
        super(in);
        if (size <= 0) {
            throw new IllegalArgumentException("Buffer size <= 0");
        }
        buf = new byte[size];
    }

    /**
     * 用更多的数据填充缓冲区,考虑到shuffling和其他处理标记的技巧，
     * 假设它是由同步方法调用的。该方法还假设所有数据已经被读入，因此pos >count。
     */
    private void fill() throws IOException {
    	// 得到内部缓冲区buffer
        byte[] buffer = getBufIfOpen();
        // 没有mark的情况下， pos为0
        if (markpos < 0)
            pos = 0;            /* no mark: throw away the buffer */
        // pos >= buffer.length  buffer已经被读取完了 
        else if (pos >= buffer.length)  /* no room left in buffer */
        	// markpos > 0  有标记，标记处在缓存中间
            if (markpos > 0) {  /* can throw away early part of the buffer */
            	// 把buffer中，markpos到pos的部分移动到0-sz处，pos设置为sz，markpos为0
                int sz = pos - markpos;
                System.arraycopy(buffer, markpos, buffer, 0, sz);
                pos = sz;
                markpos = 0;
                // markpos已经为0了，marklimit比buffer.length小，再读取buffer已经没有地方了
            } else if (buffer.length >= marklimit) {
            	// 清空缓存，清空标记，markpos为-1，pos为0
                markpos = -1;   /* buffer got too big, invalidate mark */
                pos = 0;        /* drop buffer contents */
                // markpos已经为0了，marklimit比buffer.length大，而buffer.length已经最大了，不能扩容
            } else if (buffer.length >= MAX_BUFFER_SIZE) {
                throw new OutOfMemoryError("Required array size too large");
               // markpos已经为0了，marklimit比buffer.length大
            } else {            /* grow buffer */
            	// 建立一个长度为min(2*pos,marklimit,MAX_BUFFER_SIZE),的缓存数组，然后把原来0-pos移动到新数组的0-pos处
                int nsz = (pos <= MAX_BUFFER_SIZE - pos) ?
                        pos * 2 : MAX_BUFFER_SIZE;
                if (nsz > marklimit)
                    nsz = marklimit;
                byte nbuf[] = new byte[nsz];
                System.arraycopy(buffer, 0, nbuf, 0, pos);
                // 用bufUpdater替换buffer
                if (!bufUpdater.compareAndSet(this, buffer, nbuf)) {
                    // Can't replace buf if there was an async close.
                    // Note: This would need to be changed if fill()
                    // is ever made accessible to multiple threads.
                    // But for now, the only way CAS can fail is via close.
                    // assert buf == null;
                    throw new IOException("Stream closed");
                }
                buffer = nbuf;
            }
        // 当前读取上限count为pos
        count = pos;
        // 从内部的输入流，读取pos到buffer.length部分，读取的字节数加到count
        int n = getInIfOpen().read(buffer, pos, buffer.length - pos);
        if (n > 0)
            count = n + pos;
    }

    /**
     * 请参阅InputStream的read方法的一般约定。
     *
     * @return     the next byte of data, or <code>-1</code> if the end of the
     *             stream is reached.
     * @exception  IOException  if this input stream has been closed by
     *                          invoking its {@link #close()} method,
     *                          or an I/O error occurs.
     * @see        java.io.FilterInputStream#in
     */
    public synchronized int read() throws IOException {
    	// 如果pos >= count，缓存已经读完
        if (pos >= count) {
        	// 调用fill方法，读取更多的数据到缓存
            fill();
            // 如果pos还是 >= count，说明内部的输入流已经读完，返回-1
            if (pos >= count)
                return -1;
        }
        // 返回buffer[pos]的byte，然后pos++
        return getBufIfOpen()[pos++] & 0xff;
    }

    /**
     * 将字符读入数组的一部分，必要时最多从底层流读取一次。
     */
    private int read1(byte[] b, int off, int len) throws IOException {
    	// 目前buffer中可读取的长度avail
        int avail = count - pos;
        if (avail <= 0) {
            /* 如果请求的长度至少和缓冲区一样大，并且没有mark/reset活动，
               那么就不必麻烦地将字节复制到本地缓冲区中。
               通过这种方式，缓冲流将无害地级联。
               */
        	// 如果len >= buffer.length并且没有标记
            if (len >= getBufIfOpen().length && markpos < 0) {
            	// 直接从内部输入流读取数据
                return getInIfOpen().read(b, off, len);
            }
            // 可读取的长度avail小于等于0，fill方法读取，如果还是小于等于0，说明没有可读取字节，返回-1
            fill();
            avail = count - pos;
            if (avail <= 0) return -1;
        }
        // 此时缓存数组中有可读取的数据，读取min(avail,len)的数据，然后pos移动cnt位
        int cnt = (avail < len) ? avail : len;
        System.arraycopy(getBufIfOpen(), pos, b, off, cnt);
        pos += cnt;
        return cnt;
    }

    /**
     * 从这个字节输入流中读取字节到指定的字节数组中，从给定的offset开始。
     *
     * <p> 这个方法实现了InputStream类对应的read(byte[], int, int)方法的一般约定。
     * 另外，它通过重复调用底层流的read方法来尝试读取尽可能多的字节。
     * 迭代read继续，直到下列条件之一为真:<ul>
     *
     *   <li>已被读取的指定字节数
     *
     *   <li>底层流的read方法返回-1，表示文件结束
     *
     *   <li> 底层流的available方法返回0，表示进一步的输入请求将被阻塞
     *
     * </ul> 如果底层流上的第一次读取返回-1以表示文件结束，则此方法返回-1。
     * 否则，此方法将返回已读取的字节数。
     *
     * <p> 这个类的子类鼓励(但不是必需的)尝试以相同的方式读取尽可能多的字节。
     *
     * @param      b     destination buffer.
     * @param      off   offset at which to start storing bytes.
     * @param      len   maximum number of bytes to read.
     * @return     the number of bytes read, or <code>-1</code> if the end of
     *             the stream has been reached.
     * @exception  IOException  if this input stream has been closed by
     *                          invoking its {@link #close()} method,
     *                          or an I/O error occurs.
     */
    public synchronized int read(byte b[], int off, int len)
        throws IOException
    {
        getBufIfOpen(); // Check for closed stream
        if ((off | len | (off + len) | (b.length - (off + len))) < 0) {
            throw new IndexOutOfBoundsException();
        } else if (len == 0) {
            return 0;
        }

        int n = 0;
        // 不断循环
        for (;;) {
        	// 尝试将数据读入b中，长度为len-n，位置从off+n开始
            int nread = read1(b, off + n, len - n);
            // 如果这次读取的字节数小于等于0，返回总共读取的字节数n
            if (nread <= 0)
                return (n == 0) ? nread : n;
            // n加上本次读取的字节数nread
            n += nread;
            // 如果n大于需要读取数len，返回n
            if (n >= len)
                return n;
            // 如果input没有关闭，但是input的available方法返回小于等于0，返回n
            InputStream input = in;
            if (input != null && input.available() <= 0)
                return n;
        }
    }

    /**
     * 参见InputStream的skip方法的一般约定。
     *
     * @exception  IOException  if the stream does not support seek,
     *                          or if this input stream has been closed by
     *                          invoking its {@link #close()} method, or an
     *                          I/O error occurs.
     */
    public synchronized long skip(long n) throws IOException {
        getBufIfOpen(); // Check for closed stream
        if (n <= 0) {
            return 0;
        }
        // buffer中可读取数目avail
        long avail = count - pos;

        // 如果buffer没有多余的字节
        if (avail <= 0) {
            // 如果没有标记，那么不把读取的保存在buffer中，直接从底层流skip
            if (markpos <0)
                return getInIfOpen().skip(n);

            // 如果有标记，调用fill方法，avail为现在可读取的字节数
            fill();
            avail = count - pos;
            // 如果buffer还是没有多余的字节，返回0
            if (avail <= 0)
                return 0;
        }
        // 跳过的数目为min(avail,n)
        long skipped = (avail < n) ? avail : n;
        // pos加上skipped，相当于read了skipped字节
        pos += skipped;
        return skipped;
    }

    /**
     * 返回可以从该输入流读取(或跳过)的字节数的估计数，而不会被该输入流的方法的下一次调用阻塞。
     * 下一个调用可能是同一个线程或另一个线程。
     * 单个读取或跳过这许多字节不会阻塞，但可能读取或跳过更少的字节。
     * <p>
     * 这个方法返回buffer中剩余要读取的字节数(count - pos) + 调用in.available()的结果。
     *
     * @return     an estimate of the number of bytes that can be read (or skipped
     *             over) from this input stream without blocking.
     * @exception  IOException  if this input stream has been closed by
     *                          invoking its {@link #close()} method,
     *                          or an I/O error occurs.
     */
    public synchronized int available() throws IOException {
    	// n为buffer中剩余的字节数，avail为底层输入流的available
        int n = count - pos;
        int avail = getInIfOpen().available();
        // 返回min(n + avail,Integer.MAX_VALUE)
        return n > (Integer.MAX_VALUE - avail)
                    ? Integer.MAX_VALUE
                    : n + avail;
    }

    /**
     * 参见InputStream的mark方法的一般约定。
     *
     * @param   readlimit   the maximum limit of bytes that can be read before
     *                      the mark position becomes invalid.
     * @see     java.io.BufferedInputStream#reset()
     */
    public synchronized void mark(int readlimit) {
    	// 设置marklimit和markpos
        marklimit = readlimit;
        markpos = pos;
    }

    /**
     * 参见InputStream的reset方法的一般约定。
     * <p>
     * 如果markpos为-1(没有标记被设置或标记被无效)，IOException被抛出。
     * 否则，将pos设置为等于markpos。
     *
     * @exception  IOException  if this stream has not been marked or,
     *                  if the mark has been invalidated, or the stream
     *                  has been closed by invoking its {@link #close()}
     *                  method, or an I/O error occurs.
     * @see        java.io.BufferedInputStream#mark(int)
     */
    public synchronized void reset() throws IOException {
        getBufIfOpen(); // Cause exception if closed
        if (markpos < 0)
            throw new IOException("Resetting to invalid mark");
        // 设置pos为markpos
        pos = markpos;
    }

    /**
     * 测试此输入流是否支持reset和mark方法。
     * BufferedInputStream的markSupported方法返回true。
     *
     * @return  a <code>boolean</code> indicating if this stream type supports
     *          the <code>mark</code> and <code>reset</code> methods.
     * @see     java.io.InputStream#mark(int)
     * @see     java.io.InputStream#reset()
     */
    public boolean markSupported() {
        return true;
    }

    /**
     * 关闭该输入流并释放与该流关联的任何系统资源。
     * 一旦流被关闭，进一步的read()、available()、reset()或skip()调用将抛出一个IOException。
     * 关闭以前关闭的流没有任何效果。
     *
     * @exception  IOException  if an I/O error occurs.
     */
    public void close() throws IOException {
        byte[] buffer;
        // 当buffer不为null时，循环操作
        while ( (buffer = buf) != null) {
        	// 如果用bufUpdater将this的buffer设置为null成功
            if (bufUpdater.compareAndSet(this, buffer, null)) {
            	// 先设置in为null，然后调用底层输入流的close方法
                InputStream input = in;
                in = null;
                if (input != null)
                    input.close();
                return;
            }
            // 如果没有成功，可能在fill方法中设置了一个新的buf，进入下一个循环
        }
    }
}
